////////////////////////////////////////////////////////////////////////////////////////
//
// JSimpleSim - A machine language simulator based on SimpSim by Anne-Gert Bultena.
//
// Copyright (C) 2008 Gerardo Camorlinga, Ryan Fitzgerald, and RJ Gordon
//
// This file is part of JSimpleSim.
//
// JSimpleSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// JSimpleSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with JSimpleSim.  If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////////////

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jsimplesim.model;

import java.util.Arrays;

import javax.swing.table.*;
import org.apache.log4j.Logger;
import jsimplesim.view.JSimpleSimView;

/**
 * A custom table model for our hex table component. Facilitates the storage of
 * hex digits into bytes, so that they can be easily serialized. Also handles
 * the storage and retrieval of our data.
 * 
 * @author f08maroon
 */
public class RegHexTableModel extends AbstractTableModel {

    /**
	 * 
	 */
	private static final long serialVersionUID = -5581241457796461237L;
	static Logger logger = Logger.getLogger(RegHexTableModel.class);
    private static final String HEX = "0123456789ABCDEF";
    private static final int CHARACTERS_PER_ROW = 1;
    private static String[] columnNames = {""};
    private boolean[][] newItem;
    private boolean isAddress = true;
    private byte[] buffer;
    private static JSimpleSimView view;

    /**
     * Default Constructor. Initializes the model to all zero's just like the
     * SimpleSim version.
     * @param view1
     */
    public RegHexTableModel(JSimpleSimView view1) {

    	// Team Alpha edit:
    	// altered the array to ensure both register D (Stack Pointer)
    	// and register E (Base Pointer) now initialize to FF
        buffer = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0};
        this.resetBuffer(buffer);
        view = view1;
        setNewItem(new boolean[16][1]);

    }

    /**
     * Sets the data model to a new model. Used when loading binary files, or
     * clearing the contents of the view.
     *
     * @param buffer
     *            An array of bytes with the data we want. Has to be 256 in
     *            length.
     *
     */
    public void resetBuffer(byte[] buffer) {
        this.buffer = buffer;
        fireTableDataChanged();
    }

    /**
     * Getter method for our data.
     *
     * @return An array of bytes containing our data.
     */
    public byte[] getBuffer() {
        return this.buffer;
    }
    
    @Override
	public String toString() {
		return "RegHexTableModel [newItem=" + Arrays.toString(newItem)
				+ ", isAddress=" + isAddress + ", buffer="
				+ Arrays.toString(buffer) + "]";
	}

	/**
     * Getter method for the number of columns in our model.
     *
     * @return The number of columns.
     */
    public int getColumnCount() {
        return columnNames.length;
    }

    /**
     * Getter method for the number of rows in our model.
     *
     * @return The number of rows.
     */
    public int getRowCount() {
        if (buffer == null) {
            return 0;
        } else {
            return (int) Math.ceil(buffer.length / (float) CHARACTERS_PER_ROW);
        }
    }

    /**
     * Getter method for a specific column of our model.
     *
     * @param col
     *            The index of the column in the model.
     * @return The name of the column.
     */
    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }

    /**
     * Getter method for a data element in the model.
     *
     * @param row
     *            The row index of the element.
     * @param col
     *            The column index of the element.
     * @return The element in the form of an Object. It's actually a String.
     */
    public Object getValueAt(int row, int col) {

        int index = (row * CHARACTERS_PER_ROW);

        if (index < buffer.length) {
            return byteToHex(buffer[index]);
        } else {
            return null;
        }
    }

    /**
     * Getter method that gets the type of a column.
     *
     * @param c
     *            Not used for anything
     * @return All columns are of type String so returns String.class.
     */
    @Override
    public Class<String> getColumnClass(int c) {
        return String.class;
    }

    /**
     * Checks to see whether a cell is editable or not. Depending on whether the
     * cell is under the address column or not.
     *
     * @param row
     *            The row index.
     * @param col
     *            The column index.
     * @return Whether or not cell is editable.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        if (col == 0) {
            return true;
        }
        int index = (row * CHARACTERS_PER_ROW);

        if (index < buffer.length) {
            return true;
        } else {
            return false;
        }
    }

    /*	@Override
    public void addTableModelListener(TableModelListener reg) {
    super.addTableModelListener(reg);
    fireTableDataChanged();
    }*/
    /**
     * Updates the model with changes to the view.
     *
     * @param value
     *            The updated value of the view.
     *
     * @param row
     *            The index of the cell's row.
     * @param col
     *            The index of the cell's column.
     */
    @Override
    public void setValueAt(Object value, int row, int col) {
        String hexString = value.toString();
        String oldValue = (String) getValueAt(row, col);
        int index = (row * CHARACTERS_PER_ROW);

        if (!oldValue.equals(hexString) || index == 15) {
            this.clearOldItems();
            getNewItem()[row][col] = true;

            if (hexString.length() == 1) {
                hexString = '0' + hexString;
            }

            logger.info("hexString = " + hexString);

            if (hexString.length() > 0) {
                byte hexByte = (byte) Integer.parseInt(hexString, 16);
                logger.info("hexByte = " + hexByte);


                // logger.info("index = " + index);
                // logger.info("buffer length = " + buffer.length);
                // logger.info("column count = " + getColumnCount());
                buffer[index] = hexByte;

                this.fireTableDataChanged(); // fireTableDataChanged();
                logger.debug("Register table cell has been updated");

                if (index == 15)// if RF was changed
                {

                    // output to display area
                    // char[] chars = {hexString.charAt(0),
                    // hexString.charAt(1)};

                    byte[] data = new byte[1];
                    data[0] = hexByte;
                    try {
                        // String asciiString = new String(data, "ISO-8859-1");
                        String asciiString = new String(data, "ISO-8859-1");
                        logger.debug("output string is: " + asciiString);
                        view.output(asciiString);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    /**
     * Changes a byte into a string.
     *
     * @param value
     *            The byte to convert.
     * @return The string representation of the byte.
     */
    private String byteToHex(int value) {
        int hi = (value & 0xF0) >> 4;
        int lo = (value & 0x0F);
        return "" + HEX.charAt(hi) + HEX.charAt(lo);
    }

    /**
     * Changes an int into a string.
     *
     * @param value
     *            The int to convert.
     * @return The string representatin of the int.
     */
    @SuppressWarnings("unused")
	private String intToHex(int value) {
        int hi = (value & 0xFF00) >> 8;
        int lo = (value & 0x00FF);
        return byteToHex(hi) + byteToHex(lo);
    }

    /**
     *
     * @param string
     * @return
     */
    public static byte[] fromHexString(String string) {
        /*
         * byte[] data = new byte[1];
         *
         * for (int i = 0; i < data.length; i++) { data[i] = HEXDATA(chars[i <<
         * 1], chars[(i << 1) + 1]); } return data;
         */
        byte[] data = new byte[1];
        int a = Integer.parseInt(string, 16);
        data[0] = (byte) a;
        return data;
    }

    private void clearOldItems() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 16; i++) {
            getNewItem()[i][0] = false;
        }
    }

    /**
     *
     * @param newItem
     */
    public void setNewItem(boolean[][] newItem) {
        this.newItem = newItem;
    }

    /**
     *
     * @return
     */
    public boolean[][] getNewItem() {
        return newItem;
    }
}
